<?php

namespace app\common\services;

use app\common\model\Type;
use JsonSerializable;

class TypeNode implements JsonSerializable
{
    /**
     * @var int
     */
    public $id;

    /**
     * @var TypeNode
     */
    public $parent;

    /**
     * @var TypeNode[]
     */
    public $children;

    /**
     * @var int
     */
    public $height;

    /**
     * @var Type
     */
    public $node;

    /**
     * @var int
     */
    public $validRootTypeId;

    /**
     * @var int
     */
    public $validOutputCode;

    /**
     * @var bool 有效性
     */
    public $consistent;

    public function __construct(&$types, $type = null, $parent = null)
    {
        $this->node = $type;
        /** @var TypeNode $parent */
        $this->parent = $parent;

        $typeId = $type === null ? 0 : $type->id;
        $this->id = $typeId;

        $childrenTypes = array_filter($types, function ($item) use ($typeId) {
            /** @var Type $item */
            return $item->parent_type_id === $typeId;
        });

        if ($parent !== null && $this->node !== null) {
            // 不是<ROOT>节点
            if ($parent->node !== null) {
                // 不是第一层节点（根类型）
                /** @var TypeNode $parent */
                $this->consistent = $parent->consistent
                    && $this->node->root_type_id === $parent->node->root_type_id
                    && $this->node->output_code === $parent->node->output_code;
            } else {
                // 是根类型节点，要求根类型ID跟自身相同
                $this->consistent = $this->node->root_type_id === $this->node->id;
            }
        } else {
            $this->consistent = true; // 初始值为true，之后会被刷新
        }

        if ($parent !== null && $this->node !== null) {
            if ($parent->node !== null) {
                $this->validRootTypeId = $parent->validRootTypeId;
                $this->validOutputCode = $parent->validOutputCode;
            } else {
                $this->validRootTypeId = $this->node->id;
                $this->validOutputCode = $this->node->output_code;
            }
        } else {
            $this->validRootTypeId = null;
            $this->validOutputCode = null;
        }

        $this->children = array_map(
            function ($item) use ($types) {
                // 递归构建树
                return new self($types, $item, $this);
            },
            $childrenTypes
        );

        if (count($this->children) > 0) {
            $this->height = max(array_map(function ($node) {
                /** @var TypeNode $node */
                return $node->height;
            }, $this->children)) + 1;
        } else {
            $this->height = 0;
        }

        $this->consistent = array_reduce(
            $this->children,
            function ($flag, $item) {
                /** @var TypeNode */
                return $flag && $item->consistent;
            },
            $this->consistent
        );
    }

    /**
     * 将合法的root_type_id和output_code保存到数据库
     */
    public function freshData()
    {
        if ($this->validOutputCode !== null && $this->validRootTypeId !== null) {
            $this->node->root_type_id = $this->validRootTypeId;
            $this->node->output_code = $this->validOutputCode;
            $this->node->save();
        }
        foreach ($this->children as $item) {
            $item->freshData();
        }
    }

    public function getTypeName()
    {
        return $this->node ? $this->node->type_name : '<ROOT>';
    }

    public function jsonSerialize()
    {
        $data = [
            'id' => $this->id,
            'type_name' => $this->getTypeName(),
            'height' => $this->height,
            'consistent' => $this->consistent,
            'validRootTypeId' => $this->validRootTypeId,
            'validOutputCode' => $this->validOutputCode,
        ];
        if (count($this->children) > 0) {
            $data['children'] = $this->children;
        }
        return $data;
    }

    public function toStringLines($tab = 0)
    {
        $childrenLines = [];
        $childrenLines[] = str_repeat('  ', $tab) . $this->getTypeName();
        foreach ($this->children as $item) {
            $childrenLines = array_merge(
                $childrenLines,
                $item->toStringLines($tab + 1)
            );
        }
        return $childrenLines;
    }

    public function __toString()
    {
        return \implode("\n", $this->toStringLines());
    }
}
